package org.javaboy.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * LogAdvisor 就是切面=切点+通知
 */
//当前 Bean 要注册到 Spring 容器中
@Component
//这个类就用来说明当前类是一个切面
@Aspect
//开启自动代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class LogAdvisor {

    /**
     * 定义切点
     */
    @Pointcut("execution(* org.javaboy.demo.ICalculatorImpl.*(..))")
    public void pc1() {

    }

    /**
     * 这个是环绕通知，环绕目标方法
     *
     * @param pjp
     * @return
     */
    @Around("pc1()")
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed;
        try {
            //这一句就相当于是前置通知了
            long startTime = System.currentTimeMillis();
            //这句话其实就是执行目标方法
            proceed = pjp.proceed();
            //这里就是后置通知了
            long endTime = System.currentTimeMillis();
            System.out.println(pjp.getSignature().getName() + " 方法执行耗时 " + (endTime - startTime) + " 毫秒");
        } catch (Throwable e) {
            //这里就是异常通知
            //xxx
            throw new RuntimeException(e);
        }
        return proceed;
    }

    /**
     * 前置通知
     */
    @Before("pc1()")
    public void before(JoinPoint jp) {
        //获取目标方法的名字
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法开始执行了");
    }

    /**
     * 后置通知
     *
     * @param jp
     */
    @After("pc1()")
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行结束了");
    }

    /**
     * 返回通知执行的时候，实际上已经知道目标方法的返回值是什么了
     * 这个方法的第二个参数，就是目标方法的返回值。
     * 注意第二个方法的参数，这个参数只有在和目标方法的返回值类型相匹配的时候，当前方法才会被触发
     *
     * @param jp
     */
    @AfterReturning(value = "pc1()",returning = "val")
    public void afterReturning(JoinPoint jp, Object val) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行返回 " + val);
    }

    /**
     * 这个是目标方法抛出异常的时候被触发。
     * 当抛出异常的时候，我们希望能够在这个方法中得知发生了什么
     * 注意异常类型
     *
     * @param jp
     */
    @AfterThrowing(value = "pc1()",throwing = "t")
    public void afterThrowing(JoinPoint jp, Throwable t) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行抛出 " + t.getMessage());
    }
}
